#pragma once
#include <string>
#include <map>
#include <list>
#include <google/sparse_hash_map>
#include "AdderString.h"

#ifdef VALUE_ALLOC_DEBUG
extern long int valuesAllocated;
#endif

// Pre-import class definitions
class AdderObject;
class AdderFunction;
class AdderVariableScoper;

// Value objects
enum AdderValueType
{
	UNSET,
	NUMBER,
	STRING,
	ARRAY,
	OBJECT
};

/*

This was originally defined as a struct, but because the arrays
are treated as references (e.g. when we write to one in a register
it must also update the variable), sometimes the array would not
have been initalized properly, and would cause an exception for
accessing 0xcdcdcdcd (which is the VC++ address for uninitalized
variable).  Therefore, by changing it to a class and adding a
constructor, we can ensure that all the variables are initalized
correctly and there are no problems iterating through arrays.

*/
class AdderValue
{
public:
	AdderValue();
	AdderValue(const AdderValue& c);
	~AdderValue();
	AdderValue& operator =(const AdderValue& b);

	AdderValueType type;
	float r_number;
	std::map<float, AdderValue*>*& a_array();
	AdderString& a_string(bool replacement = false);
	AdderObject * r_object;

private:
	bool b_array;
	bool o_array;
	bool b_string;
	std::map<float, AdderValue*> * r_array;
	AdderString * r_string;
};

class AdderBytecodeEngine;

// Builtins Function Definition
typedef void(*BuiltinInitalizer)(AdderBytecodeEngine *);
typedef void(*BuiltinScopeChangeFunction)(AdderBytecodeEngine *);
typedef AdderValue*(*BuiltinFunction0)(AdderBytecodeEngine *);
typedef AdderValue*(*BuiltinFunction1)(AdderBytecodeEngine *, AdderValue* arg0);
typedef AdderValue*(*BuiltinFunction2)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1);
typedef AdderValue*(*BuiltinFunction3)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2);
typedef AdderValue*(*BuiltinFunction4)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2, AdderValue* arg3);
typedef AdderValue*(*BuiltinFunction5)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2, AdderValue* arg3, AdderValue* arg4);
typedef AdderValue*(AdderObject::*BuiltinClassFunction0)(AdderBytecodeEngine *);
typedef AdderValue*(AdderObject::*BuiltinClassFunction1)(AdderBytecodeEngine *, AdderValue* arg0);
typedef AdderValue*(AdderObject::*BuiltinClassFunction2)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1);
typedef AdderValue*(AdderObject::*BuiltinClassFunction3)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2);
typedef AdderValue*(AdderObject::*BuiltinClassFunction4)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2, AdderValue* arg3);
typedef AdderValue*(AdderObject::*BuiltinClassFunction5)(AdderBytecodeEngine *, AdderValue* arg0, AdderValue* arg1, AdderValue* arg2, AdderValue* arg3, AdderValue* arg4);

// Mapping pairs
typedef std::pair<const AdderString, AdderValue*> string_value_map;
typedef std::pair<const int, AdderValue*> int_value_map;
typedef std::pair<const AdderString, AdderObject*> string_class_map;
typedef std::pair<const AdderString, AdderFunction*> string_function_map;
typedef std::pair<const AdderString, BuiltinScopeChangeFunction> string_scopefunction_map;
typedef std::pair<const float, AdderValue*> float_value_map;
typedef std::pair<const int, google::sparse_hash_map<AdderString, AdderValue*>*> int_map_map;
typedef std::pair<const int, std::vector<AdderValue*>> int_imap_map;
typedef std::pair<const int, unsigned int> function_argument_map;

class AdderBytecodeEngine
{
public:
	AdderBytecodeEngine(unsigned int variable_scope_size, unsigned int register_scope_size);
	~AdderBytecodeEngine(void);

	// Function Handling
	bool assembly_jump_required;
	void * assembly_jump_class_position;
	bool breaking;
	void ScopeCleanup();

	// Debugging
	bool debug;

	// Builtin Initalization / Registration
	void BuiltinLoadLibrary(BuiltinInitalizer func);
	void BuiltinRegisterScopeChange(AdderString name, BuiltinScopeChangeFunction func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction0 func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction1 func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction2 func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction3 func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction4 func);
	void BuiltinRegisterFunction(AdderString name, BuiltinFunction5 func);
	void BuiltinRegisterClass(AdderString name, AdderObject* class_def);
	void BuiltinRegisterVariable(AdderString name, AdderValue* var);

	// User Functions / Classes Registration
	void UserRegisterFunction(AdderString name, std::vector<unsigned int> arguments);
	void UserRegisterClassFunction(AdderString name, AdderString clsname, void * jmp, std::vector<unsigned int> arguments);

	// Bytecode: Register Creation
	void NEWC	(int reg, const char* cls);
	void NEWA	(int reg);
	void NEWS	(int reg, const char* str);
	void NEWN	(int reg, float number);

	// Bytecode: Register / Variable Movement and Basic Comparisons
	void RMOVV	(int reg, int var);
	void RMOVA	(int reg, int var, float index);
	void RMOVP	(int reg, const char* prop, int object);
	void MOVV	(int reg, int var);
	void MOVA	(int reg, int var, float index);
	void MOVP	(int reg, const char* prop, int object);
	void DMOVV	(int reg, int reg2);
	void DMOVA	(int reg, int reg2, float index);
	void DDMOVA	(int reg, int reg2, int reg3);
	void DRMOVA	(int reg, int reg2, float index);
	void DRMOVP	(int reg, const char* prop, int reg2);
	void DDRMOVA(int reg, int reg2, int reg3);
	void REFA	(int reg, int var, float index);
	void REFP	(int reg, const char* prop, int object);
	void CMPV	(int res, int reg, const char* str);
	void CMPV	(int res, int reg, float number);
	void CMPR	(int res, int reg, int reg2);

	// Bytecode: Breaks
	void BVS	(const char* label, int var);
	void BVNS	(const char* label, int var);
	void BRS	(const char* label, int reg);
	void BRNS	(const char* label, int reg);
	void BT		(const char* label, int reg);
	void BF		(const char* label, int reg);
	void BU		(const char* label);
	void BERR	(const char* label, int reg,  int reg2);
	void BRK	(const char* label);

	// Bytecode: Register / Variable Cleanup
	void CLR	(int reg);
	void CLRV	(int var);

	// Bytecode: Labels / Jumps
	void LBL	(const char* label);
	void JMPR	(float position);
	void JMPA	(float position);
	void JMPL	(const char* label);

	// Bytecode: Return System
	void RET	(int reg);

	// Bytecode: Arithmetic Functions
	void ADD	(int reg, const char* str);
	void ADD	(int reg, int reg2);
	void ADD	(int reg, float value);
	void SUB	(int reg, int reg2);
	void SUB	(int reg, float value);
	void MUL	(int reg, int reg2);
	void MUL	(int reg, float value);
	void DIV	(int reg, int reg2);
	void DIV	(int reg, float value);
	void POW	(int reg, int reg2);
	void POW	(int reg, float value);

	// Bytecode: Arithmetic Comparisons
	void EQ		(int reg, int reg2);
	void NE		(int reg, int reg2);
	void LT		(int reg, int reg2);
	void LE		(int reg, int reg2);
	void GT		(int reg, int reg2);
	void GE		(int reg, int reg2);

	// Bytecode: Function Calling
	void CALLF	(const char* func, int arglist, int ret);
	void CALLC	(const char* func, int arglist, int ret, int object);

	// Bytecode: Errors
	void ERR	(float position, const char* msg);

	/* Error / exception systems */
	void _throwError(const char* bytecode_text, const char* error_text);

private:
	AdderVariableScoper * registers;
	AdderVariableScoper * variables;
	std::map<AdderString, AdderObject*> classes;
	std::map<AdderString, AdderFunction*> functions;
	std::map<AdderString, AdderString> bytecode_labels;

	// This contains the builtin scope reloader functions.
	std::map<AdderString, BuiltinScopeChangeFunction> builtins_scope_reload_functions;

	// These two variables hold the variable and register
	// dictionaries between scope changes.
	int scope_level;

	AdderValue *		return_scope;
	
	const char *	breaking_label;

	bool			breaking_catching;
	const char *	breaking_catchlabel;
	AdderObject *		breaking_catchexcp;
	int				breaking_catchregister;
	
	int				catch_errorlevel;
	std::list<AdderString> catch_errorregister;

	/* Array Recursive Comparison */
	bool arraysAreEqual(std::map<float, AdderValue*> * a, std::map<float, AdderValue*> * b, int level = 0);

	/* Register functions */
	AdderValue * _register_new(int reg);
	void _register_del(int reg);
	void _register_clean(int reg);
	bool _register_exists(int reg);
	AdderValue * _register_get(int reg);

	/* Variable functions */
	AdderValue * _variable_new(int var);
	void _variable_del(int var);
	bool _variable_exists(int var);
	AdderValue * _variable_get(int var);

	/* Class functions */
	AdderObject * _class_define(const char* cls);
	void _class_undefine(const char* cls);
	bool _class_defined(const char* cls);
	AdderObject * _class_get(const char* cls);

	/* Debugging systems */
	void _breakDebug(const char* bytecode_text);
	void _debug(const char* bytecode_text);
	void _debugMsg(const char *msg);
	const char* _debugGetTypeString(AdderValueType t);
	void _debugValue(AdderValue* m);
	void _debugValueMap(AdderVariableScoper * m, int indent = 0);
	void _debugValueMap(std::vector<AdderValue*> m, int indent = 0);
	void _debugArrayMap(std::map<float, AdderValue*> * m, int indent = 0);
	void _debugObjectMap(std::map<AdderString, AdderObject*> m, int indent = 0);
	void _debugFunctionMap(std::map<AdderString, AdderFunction*> m, int indent = 0);
	AdderString _indentPad(int indent);
};